#pragma once
#ifndef _KH_CORE_APP_H
#define _KH_CORE_APP_H
#include <Kharlia/Core/Common.h>
#include <Kharlia/Core/ObjectBase.h>
#include <list>

#undef Yield // from WinBase.h

namespace kharlia { namespace core {

class FLogger;
class CSubsystem;
class CTasker;
class CIOManager;

void _InitAppBindings();

struct FSubsystemInfo {
    sint16 Index;
    bool Remove;
    shared_ptr<CSubsystem> Ptr;
    FSubsystemInfo(
        sint16 index=0,
        shared_ptr<CSubsystem> p=shared_ptr<CSubsystem>()
    ): Index(index), Remove(false), Ptr(p) {}
};
    
/// The 'kernel' of the Kharlia application, implements the main loop
/// and handles stackless yielding and sleeping. Also manages subsystems.
class KH_CORE_DECLSPEC CApp: public CObject {
    typedef std::vector<FSubsystemInfo> SubsystemMapType;
    typedef SubsystemMapType::iterator SubsystemMapIter;
    typedef SubsystemMapType::reverse_iterator SubsystemMapRevIter;

    bool mExitRequested;
    bool mExitLoop;
    bool mIsRunning;
    bool mHasRemoves;
    uint16 mThreadSleepTime;
    uint16 mTickRate;
    double mExitWaitTime;
    double mLastTickTime;
    double mTime;
    float mDeltaTime;
    uint32 mTickCount;
    SubsystemMapType mSubsystemsToAdd;
    SubsystemMapType mSubsystems;
    shared_ptr<CIOManager> mIOManager;
    shared_ptr<CTasker> mTasker;

    void TickSubsystems(float deltaTime);

    static CApp* msInstance;

    KH_DECL_GC()

public:
    CApp(PyObject* self);
    ~CApp();

    void _Run();
    void _RequestExit();
    void RequestExit();
    void ForceExit();
    void Tick();
    // Subsystem management
    void AddSubsystem(sint16, shared_ptr<CSubsystem>);
    void RemoveSubsystem(shared_ptr<CSubsystem>);

    KH_DEF_GETSET(uint16, ThreadSleepTime)
    KH_DEF_GETSET(double, ExitWaitTime)
    KH_DEF_GET(float, DeltaTime)
    KH_DEF_GET(double, Time)
    KH_DEF_GET(uint32, TickCount)
    KH_DEF_GET(shared_ptr<CIOManager>, IOManager)
    KH_DEF_GET(shared_ptr<CTasker>, Tasker)
    KH_DEF_GET(uint16, TickRate)
    inline void SetTickRate(uint16 value) { mTickRate = (value < 0)? 0: value; }

    list GetSubsystemList();
    object CreateTasklet(object callable=object());

    static shared_ptr<CApp> GetInstance();
    static CApp* GetInstancePtr();

    friend void _InitAppBindings();
};

inline void CApp::RequestExit() {
    call_method<void>(mSelf, "RequestExit");
}

inline object CApp::CreateTasklet(object callable) {
    return call_method<object>(mSelf, "CreateTasklet", callable);
}

inline shared_ptr<CApp> CApp::GetInstance() {
    assert(msInstance);
    return msInstance->GetSelfPtr<CApp>();
}

inline CApp* CApp::GetInstancePtr() {
    return msInstance;
}

inline void SetCurrentContext(str ctxt) {
    GetCurrentTasklet().attr("context") = ctxt;
}
inline void SetCurrentContext() {
    GetCurrentTasklet().attr("context") = object();
}

inline uint32 GetRealTickCount() {
#if KH_PLATF_WIN
    return ::clock(); 
#elif KH_PLATF_LINUX
#error
#endif
}

#define KH_TICKS_PER_SECOND CLOCKS_PER_SEC

inline double GetRealTime() {
    return double(GetRealTickCount()) / KH_TICKS_PER_SECOND;
}

inline void SleepThread(uint32 miliseconds) {
#if KH_PLATF_WIN
    ::Sleep(miliseconds);
#elif KH_PLATF_LINUX
    ::usleep(miliseconds * 1000);
#endif
}

}} // namespace Kharlia

KH_HAS_BACK_REFERENCE(kharlia::core::CApp)

#endif